import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:background_downloader/background_downloader.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_download_plugin/utils/download_utils.dart';
import 'package:flutter_download_plugin/utils/video/hls_segment.dart';
import 'package:flutter_download_plugin/utils/video/m3u8_download_segments.dart';
import 'package:flutter_download_plugin/utils/video/m3u8_utils.dart';
import 'package:flutter_download_plugin/utils/video/video_prevew_info.dart';
import 'package:flutter_download_plugin/utils/video/video_utils.dart';
import 'package:flutter_hls_parser/flutter_hls_parser.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

///回调方法
typedef OnPrepared = void Function(M3u8DownloaderTask task);
typedef OnStart = void Function(M3u8DownloaderTask task, int totalBytes);
typedef OnProgress = void Function(M3u8DownloaderTask task, double progress, int downloadedBytes, int totalBytes);
typedef OnFailed = void Function(M3u8DownloaderTask task, String msg);
typedef OnSuccess = void Function(M3u8DownloaderTask task);
typedef OnPaused = void Function(M3u8DownloaderTask task);

///状态变更
// typedef OnStateChange = void Function(M3u8DownloaderTask task, TaskStatus status);
///目前缺少功能
///1.下载切片完成，ts合并完成后未清理ts切片文件（已完成）
///2.删除未配置是否删除源文件(已完成)
///3.超大文件下载时，在拉取分片头文件信息的时候可能导致个别文件信息拉取不成功，无法及时进行下载
///4.ffmpeg下载时如果ffmpeg失败（概率低）无法及时回调
///5.在ffmpeg下载时如果取消下载，任务回调的是下载成功
///6.ts分片文件时加密流，暂不支持解密下载
///7.未显示下载速度(已完成)
///8.ffmpeg托管下载时，暂停下载后无法恢复下载(已完成)
class M3u8Downloader {
  // 单例的静态实例，通过工厂构造函数访问
  static M3u8Downloader? _instance;

  // 私有的构造函数，防止外部创建类的实例
  M3u8Downloader._privateConstructor() {
    init();
  }

  // 提供访问单例的工厂方法
  factory M3u8Downloader() {
    // 如果实例不存在，则创建新的实例
    _instance ??= M3u8Downloader._privateConstructor();

    return _instance!;
  }

  late SharedPreferences sharedPreferences;

  int maxConcurrentDownloads = 2;

  ///当前所有列表的队列
  List<M3u8DownloaderTask> _taskQueue = [];

  // final Map<String, CancelToken> _cancelTokens = {};
  String baseDir = "";

  ///限制回调频率用
  bool canCallbackProgress = true;

  Timer? progressTimer;

  ///准备下载
  OnPrepared? onPrepared;

  ///开始下载，在基础信息处理完后才会回调此方法
  OnStart? onStartDownload;
  OnProgress? onProgress;
  OnFailed? onFailed;
  OnSuccess? onSuccess;
  OnPaused? onPaused;

  // OnStateChange? onStateChange;

  ///设置监听器
  void setListener({
    required OnPrepared onPrepared,
    required OnStart onStart,
    required OnProgress onProgress,
    required OnFailed onFailed,
    required OnSuccess onSuccess,
    required OnPaused onPaused,
    // required OnStateChange onStateChange,
  }) {
    this.onPrepared = onPrepared;
    this.onStartDownload = onStart;
    this.onProgress = onProgress;
    this.onFailed = onFailed;
    this.onSuccess = onSuccess;
    this.onPaused = onPaused;
    // this.onStateChange = onStateChange;
  }

  ///初始化基础配置信息
  Future<void> init() async {
    // Directory? directory = await getDownloadsDirectory();
    // Directory? directory = await getApplicationCacheDirectory();
    Directory? directory = await getApplicationDocumentsDirectory();
    // directory ??= await getApplicationCacheDirectory();

    baseDir = "${directory.path}/download";

    sharedPreferences = await SharedPreferences.getInstance();

    await getTaskQueue();
  }

  ///添加任务到下载队列中，如果下载队列已有此任务则继续上次的下载
  void addDownloadTask({
    required M3u8DownloaderTask task,
    required Future<int> Function(List<Variant> variants) onMasterSelect,
  }) async {
    if (baseDir == "") {
      //未初始化则自动初始化
      await init();
    }

    //不添加重复任务
    M3u8DownloaderTask? findTask = null;
    for (var t in _taskQueue) {
      if (t.url == task.url) {
        findTask = t;
        break;
      }
    }
    if (findTask != null) {
      if (findTask.status == TaskStatus.running) {
        return;
      }

      resumeDownload(url: findTask.url);
      return;
    }

    // if (_taskQueue.any((t) {
    //   if (t.url == task.url) {
    //     //url 相同且已经拉取到过基础信息
    //     t.status = TaskStatus.enqueued;
    //     return true;
    //   }
    //   return false;
    // })) {
    //   if (task.status == TaskStatus.running) {
    //     return;
    //   }
    //
    //   resumeDownload(url: task.url);
    //   return;
    // }

    ///生成下载基本信息
    String directoryName = VideoUtils.generateMd5(task.url);
    task.directory = "$baseDir/$directoryName"; //每个m3u8文件都放在一个文件夹内
    task.fileName = "$directoryName.${VideoUtils.getSuffix(task.url)}";
    task.filePath = "${task.directory}/${task.fileName}";
    task.creationTimeStamp = DateTime.now().millisecondsSinceEpoch;
    task.creationTime = VideoUtils.timestampToLocalTime(task.creationTimeStamp);
    task.queue = directoryName;
    task.status = TaskStatus.notFound;

    _taskQueue.add(task);
    if (onPrepared != null) {
      onPrepared!(task);
    }
    await attachM3u8Info(task: task, onMasterSelect: onMasterSelect);

    task.status = TaskStatus.enqueued;
    _tryStartNextTask();
  }

  ///抓取视频的基础信息
  Future<void> attachM3u8Info({
    required M3u8DownloaderTask task,
    required Future<int> Function(List<Variant> variants) onMasterSelect,
  }) async {
    //读取基础信息
    VideoPreviewInfo info = await M3u8Utils.getM3u8InfoByUrl(url: task.url, onMasterSelect: onMasterSelect);

    if (info.segments.isEmpty) {
      //没有分片信息认为解析失败
      return;
    }
    task.segmentsIndex = info.segmentsIndex;
    task.segments = info.segments;
    task.variantUrl = info.url;
    task.width = info.width;
    task.height = info.height;
    task.bitrate = info.bitrate;
    task.duration = info.duration;
    task.mediaPlaylistUrls = info.mediaPlaylistUrls;

    Directory(task.directory).createSync(recursive: true); //创建目录
    //保存m3u8的信息到本地文件
    File file = File("${task.directory}/remote.m3u8");
    file.createSync(recursive: true);
    await file.writeAsString(info.m3u8Info);
  }

  // 尝试启动下一个任务
  void _tryStartNextTask() async {
    if (findRunningTask() >= maxConcurrentDownloads || _taskQueue.isEmpty) {
      saveTaskQueue();
      return;
    }

    M3u8DownloaderTask? task = findWaitTask();

    if (task == null) {
      if (kDebugMode) {
        print("当前无等待任务");
      }
      return;
    }
    if (task.status == TaskStatus.paused) {
      ///开始任务是有延时的，所以在启动任务的时候判断是否是可以开始下载的状态不是的话就尝试获取下一个任务
      _tryStartNextTask();
      return;
    }
    task.status = TaskStatus.running;
    saveTaskQueue();

    try {
      // await Future.delayed(const Duration(seconds: 2)); // 模拟 2 秒延迟

      task.m3u8downloadSegments = M3u8DownloadSegments(
        parentTask: task,
        onFailed: (String msg) {
          task.status = TaskStatus.failed;
          if (onFailed != null) {
            onFailed!(task, msg);
          }
        },
        onSuccess: (String fileMp4Path, double progress, int downloadedBytes, int totalBytes, int downloadedSegments, int totalSegments) {
          task.status = TaskStatus.complete;
          task.progress = progress;
          task.totalLength = totalBytes;
          task.downloadedBytes = downloadedBytes;
          task.downloadedSegments = downloadedSegments;
          task.totalSegments = totalSegments;
          task.filePath = fileMp4Path;
          task.fileName = VideoUtils.getFileNameWithoutExtension(fileMp4Path);

          if (onSuccess != null) {
            onSuccess!(task);
          }
          saveTaskQueue();

          _tryStartNextTask();
        },
        onStartDownload: (int totalBytes, int totalSegments) {
          task.status = TaskStatus.running;
          task.totalLength = totalBytes;
          task.totalSegments = totalSegments;

          if (onStartDownload != null) {
            onStartDownload!(task, totalBytes);
          }
        },
        onProgress: (double progress, int downloadedBytes, int totalBytes, int downloadedSegments, int totalSegments, int speed) {
          task.status = TaskStatus.running;
          task.progress = progress;
          task.totalLength = totalBytes;
          task.downloadedBytes = downloadedBytes;
          task.downloadedSegments = downloadedSegments;
          task.totalSegments = totalSegments;
          task.totalSpeed = speed;

          if (canCallbackProgress && onProgress != null) {
            canCallbackProgress = false;
            onProgress!(task, progress, downloadedBytes, totalBytes);
            Future.delayed(const Duration(milliseconds: 500), () {
              canCallbackProgress = true;
            });
          }
        },
        onPause: () {
          task.status = TaskStatus.paused;
          if (onPaused != null) {
            onPaused!(task);
          }
        },
      );
      task.m3u8downloadSegments!.startDownload();

      // LogUtils().logger.d("${task.directory}下载完成");
    } catch (e) {
      task.status = TaskStatus.failed;
      if (e is DioException && CancelToken.isCancel(e)) {
        print("Download canceled for ${task.url}");
      } else {
        print("Download error: $e");
      }
    } finally {
      saveTaskQueue();

      _tryStartNextTask();
    }
  }

  ///查询当前等待的任务
  M3u8DownloaderTask? findWaitTask() {
    for (var task in _taskQueue) {
      if (task.status == TaskStatus.enqueued) {
        return task;
      }
    }
    return null;
  }

  ///查询正在下载中的任务数量
  int findRunningTask() {
    int count = 0;
    for (var task in _taskQueue) {
      if (task.status == TaskStatus.running) {
        count++;
      }
    }
    return count;
  }

  // 暂停下载
  void pauseDownload({required String url}) {
    try {
      final task = _taskQueue.firstWhere((t) {
        if (t.url == url) {
          return true;
        }
        return false;
      });
      task.status = TaskStatus.paused;
      if (task.m3u8downloadSegments != null) {
        task.m3u8downloadSegments!.pauseAllDownload();
        task.m3u8downloadSegments = null;
      }
    } catch (e) {
      print(e);
    }
  }

  //暂停所有下载
  void pauseAllDownload() {
    for (var task in _taskQueue) {
      if (task.status == TaskStatus.running || task.status == TaskStatus.enqueued || task.status == TaskStatus.notFound) {
        task.status = TaskStatus.paused;
        if (task.m3u8downloadSegments != null) {
          task.m3u8downloadSegments!.pauseAllDownload();
        }
        // if (onPaused != null) {
        //   onPaused!(task);
        // }
      }
    }
  }

  // 恢复下载
  void resumeDownload({
    required String url,
  }) async {
    //根据url找到任务对象
    try {
      final task = _taskQueue.firstWhere((t) {
        if (t.url == url) {
          return true;
        }
        return false;
      });

      task.status = TaskStatus.enqueued;
      if (onPrepared != null) {
        onPrepared!(task);
      }

      if (task.segments == null || task.segments!.isEmpty) {
        await attachM3u8Info(
          task: task,
          onMasterSelect: (List<Variant> variants) async {
            return task.segmentsIndex;
          },
        );
      }
      _tryStartNextTask();
    } catch (e) {
      print(e);
    }
  }

  //恢复所有下载
  void resumeAllDownload() async {
    for (var task in _taskQueue) {
      if (task.status == TaskStatus.paused || task.status == TaskStatus.failed) {
        task.status = TaskStatus.enqueued;
        if (onPrepared != null) {
          onPrepared!(task);
        }
      }
    }
    for (var task in _taskQueue) {
      if (task.segments == null || task.segments!.isEmpty) {
        await attachM3u8Info(
          task: task,
          onMasterSelect: (List<Variant> variants) async {
            return task.segmentsIndex;
          },
        );
      }
      _tryStartNextTask();
    }
  }

  //缓存下载列表
  saveTaskQueue() {
    String json = jsonEncode(_taskQueue.map((task) {
      return task.toJson();
    }).toList());

    sharedPreferences.setString("downloadTaskQueue", json);
  }

  ///得到当前的下载列表
  List<M3u8DownloaderTask> getTaskQueue() {
    if (_taskQueue.isNotEmpty) {
      return _taskQueue;
    }

    String? taskQueueStr = sharedPreferences.getString("downloadTaskQueue");

    if (taskQueueStr != null) {
      List<dynamic> jsonList = jsonDecode(taskQueueStr);

      _taskQueue = jsonList.map((json) => M3u8DownloaderTask.fromJson(json as Map<String, dynamic>)).toList();

      ///初始化的时候把所有任务状态都改为暂停,因为上次下载有可能是强制被退出的
      for (var task in _taskQueue) {
        task.status = TaskStatus.paused;
      }
      return _taskQueue;
    }

    return _taskQueue;
  }

  ///清空下载记录
  void deleteAllTask({
    bool deleteFiles = true,
  }) async {
    //有进行中的任务直接暂停
    _taskQueue.forEach((t) {
      if (t.status == TaskStatus.running) {
        t.m3u8downloadSegments!.pauseAllDownload();
      }
    });

    if (deleteFiles) {
      _taskQueue.forEach((t) {
        //删除整个目录
        Directory directory = Directory(t.directory);
        if (directory.existsSync()) {
          directory.deleteSync(recursive: true);
        }
      });
    }
    _taskQueue.clear();
    saveTaskQueue();
  }

  ///删除下载任务
  void deleteTask({
    bool deleteFiles = true,
    required String url,
  }) async {
    try {
      //有进行中的任务直接暂停
      M3u8DownloaderTask task = _taskQueue.firstWhere((t) {
        return t.url == url;
      });
      if (task.m3u8downloadSegments != null) task.m3u8downloadSegments!.pauseAllDownload();

      //删除整个目录
      Directory directory = Directory(task.directory);
      if (directory.existsSync()) {
        directory.deleteSync(recursive: true);
      }

      _taskQueue.removeWhere((t) {
        return t.url == url;
      });

      saveTaskQueue();
    } catch (e) {
      print(e);
    }
  }

  List<String> urlList = [
    "https://video.twimg.com/ext_tw_video/1875835592854081536/pu/pl/rfDwbsHm-zeWZiQf.m3u8?variant_version=1&tag=12&v=d7d",
    // "https://fenvixlight88.pro/file1/hQJBMwXbxhBrISMl7VNhrqWYTpMXktmBIEJP5kyoayd00Nl7G5sqWAJKwoCkBcsTnq~M0d6p07aLa2UjRc3lZt9NwOmltFoEr6~YjnJk3xirp3OIvKEX0cHTRiTmzHQjkmbRHWWz5xtrR8DfA7WkTi9AG92oalNnFHCLq9ItvsQ=/cGxheWxpc3QubTN1OA==.m3u8",
    // "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
    "https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/hls/xgplayer-demo.m3u8",
  ];

  ///暂停下载
  void testPauseDownload() {
    for (var url in urlList) {
      M3u8Downloader().pauseDownload(url: url);
    }
  }

  ///下载测试
  void testDownload() {
    // DownloadUtils.downloadM3u8("https://fenvixlight88.pro/f ile1/hQJBMwXbxhBrISMl7VNhrqWYTpMXktmBIEJP5kyoayd00Nl7G5sqWAJKwoCkBcsTnq~M0d6p07aLa2UjRc3lZt9NwOmltFoEr6~YjnJk3xirp3OIvKEX0cHTRiTmzHQjkmbRHWWz5xtrR8DfA7WkTi9AG92oalNnFHCLq9ItvsQ=/cGxheWxpc3QubTN1OA==.m3u8");
    // String url =
    //     "https://fenvixlight88.pro/file1/hQJBMwXbxhBrISMl7VNhrqWYTpMXktmBIEJP5kyoayd00Nl7G5sqWAJKwoCkBcsTnq~M0d6p07aLa2UjRc3lZt9NwOmltFoEr6~YjnJk3xirp3OIvKEX0cHTRiTmzHQjkmbRHWWz5xtrR8DfA7WkTi9AG92oalNnFHCLq9ItvsQ=/cGxheWxpc3QubTN1OA==.m3u8";
    // String url = "https://video.twimg.com/ext_tw_video/1875835592854081536/pu/pl/rfDwbsHm-zeWZiQf.m3u8?variant_version=1&tag=12&v=d7d";
    // String url = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8";
    // String url = "http://vjs.zencdn.net/v/oceans.mp4";
    // String url = "https://ht-cdn2.adtng.com/a7/creatives/221/1559/820683/1125055/1125055_video.mp4";
    // String url = "http://mirror.aarnet.edu.au/pub/TED-talks/911Mothers_2010W-480p.mp4";
    // String url = "https://www.sample-videos.com/video321/flv/480/big_buck_bunny_480p_30mb.flv";
    // String url = "https://www.sample-videos.com/video321/mkv/720/big_buck_bunny_720p_2mb.mkv";
    // String url = "https://www.sample-videos.com/video321/3gp/144/big_buck_bunny_144p_10mb.3gp";
    // String url = "https://test-streams.mux.dev/x36xhzz/url_0/url_464/193039199_mp4_h264_aac_hd_7.ts";

    // DownloadUtils.download(
    //   urlList: [url],
    //   directory: 'temp_dir',
    //   updates: Updates.none,
    // );

    ///m3u8的信息提取
    // VideoPreviewInfo info = await M3u8Utils.getM3u8InfoByUrl(
    //   url: url,
    //   onMasterInfo: (List<Variant> variants) {
    //     return variants[0];
    //   },
    // );
    // String cover = await VideoUtils.getVideoCover(info: info);
    // print(info);

    ///除m3u8的视频信息提取
    // VideoPreviewInfo info = await VideoUtils.getVideoInfoByUrl(url: url);
    // String cover = await VideoUtils.getVideoCover(info: info);
    // print(info);

    // Directory directory = await getApplicationCacheDirectory();
    // String path = "${directory.path}/m3u8/test";
    // final controller = CachedVideoPlayerPlusController.file(File(path))
    //   ..initialize().then(
    //     (value) async {
    //       // controller.play();
    //       // setState(() {});
    //     },
    //   );

    ///m3u8下载
    // M3u8Downloader().clearTaskQueue();

    for (var url in urlList) {
      M3u8Downloader().addDownloadTask(
          task: M3u8DownloaderTask(url: url),
          onMasterSelect: (List<Variant> variants) async {
            return 0;
          });
    }

    // Timer(Duration(seconds: 5), () {
    //   M3u8Downloader.pauseDownload(url);
    //   Timer(Duration(seconds: 5), () {
    //     M3u8Downloader.resumeDownload(url: url);
    //   });
    // });
  }
}

class M3u8DownloaderTask {
  final String url;

  ///选中的分辨率的url，有多分辨率的情况下和url是不一致的，没有多分辨率的情况下有可能和url的值一致
  String variantUrl;
  String displayName;
  String fileName;
  String filePath;
  String directory;
  String creationTime;
  int creationTimeStamp;
  String group;

  ///同一个m3u8的分片文件 queue是一样的
  String queue;
  TaskStatus status;

  ///百分比进度 0-1
  double progress;

  /// 已下载字节数
  int downloadedBytes;

  ///总字节数
  int totalLength;

  ///从m3u8 url中提取出来的信息,在选择具体媒体链接后才会有值
  int width;
  int height;
  int bitrate;

  ///单位秒
  int duration;

  ///后面的数据仅存放内存，不持久化
  ///m3u8文件时的分片信息
  List<HlsSegment>? segments;

  ///有些m3u8可能会吧视频、音频、头文件等额外信息分离可能会有多个媒体链接，
  List<Uri?> mediaPlaylistUrls;

  ///下载时选择的分片链接的索引，用于恢复下载任务时使用
  int segmentsIndex;
  M3u8DownloadSegments? m3u8downloadSegments;

  ///总计下载速度（所有分片数据速度之和）
  int totalSpeed;

  ///分片数量
  int totalSegments;

  ///已下载分片数
  int downloadedSegments;

  ///dio相关的对象
  // CancelToken? cancelToken;

  final void Function(double progress)? onProgress;

  M3u8DownloaderTask({
    required this.url,
    this.variantUrl = "",
    this.displayName = "",
    this.onProgress,
    this.fileName = "",
    this.filePath = "",
    this.directory = "",
    this.creationTime = "",
    this.creationTimeStamp = 0,
    this.group = "default",
    this.queue = "",
    this.status = TaskStatus.notFound,
    this.width = 0,
    this.height = 0,
    this.bitrate = 0,
    this.duration = 0,
    this.progress = 0,
    this.downloadedBytes = 0,
    this.totalLength = 0,
    this.totalSegments = 0,
    this.downloadedSegments = 0,
    this.segmentsIndex = 0,
    this.mediaPlaylistUrls = const [],
    this.totalSpeed = 0,
  }) {
    if (displayName == "") {
      displayName = VideoUtils.getSuffix(url);
    }
  }

  factory M3u8DownloaderTask.fromJson(Map<String, dynamic> json) {
    return M3u8DownloaderTask(
      url: json['url'],
      variantUrl: json['variantUrl'],
      displayName: json['displayName'],
      fileName: json['fileName'],
      filePath: json['filePath'],
      directory: json['directory'],
      creationTime: json['creationTime'],
      creationTimeStamp: json['creationTimeStamp'],
      group: json['group'],
      queue: json['queue'],
      status: DownloadUtils.stringToTaskStatus(json['status']),
      progress: json['progress'],
      width: json['width'],
      height: json['height'],
      bitrate: json['bitrate'],
      duration: json['duration'],
      downloadedBytes: json['downloadedBytes'],
      totalLength: json['totalLength'],
      totalSegments: json['totalSegments'],
      downloadedSegments: json['downloadedSegments'],
      segmentsIndex: json['segmentsIndex'],
      totalSpeed: json['totalSpeed'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'url': url,
      'variantUrl': variantUrl,
      'displayName': displayName,
      'fileName': fileName,
      'filePath': filePath,
      'directory': directory,
      'creationTime': creationTime,
      'creationTimeStamp': creationTimeStamp,
      'group': group,
      'queue': queue,
      'status': status.toString(),
      'progress': progress,
      'width': width,
      'height': height,
      'bitrate': bitrate,
      'duration': duration,
      'downloadedBytes': downloadedBytes,
      'totalLength': totalLength,
      'totalSegments': totalSegments,
      'downloadedSegments': downloadedSegments,
      'segmentsIndex': segmentsIndex,
      'totalSpeed': totalSpeed,
    };
  }

  //对象深拷贝
  M3u8DownloaderTask copy() {
    return M3u8DownloaderTask(
      url: url,
      variantUrl: variantUrl,
      displayName: displayName,
      fileName: fileName,
      filePath: filePath,
      directory: directory,
      creationTime: creationTime,
      creationTimeStamp: creationTimeStamp,
      group: group,
      queue: queue,
      status: status,
      progress: progress,
      width: width,
      height: height,
      bitrate: bitrate,
      duration: duration,
      downloadedBytes: downloadedBytes,
      totalLength: totalLength,
      totalSegments: totalSegments,
      downloadedSegments: downloadedSegments,
      segmentsIndex: segmentsIndex,
      totalSpeed: totalSpeed,
    );
  }

  @override
  String toString() {
    return 'M3u8DownloaderTask{url: $url, variantUrl: $variantUrl, displayName: $displayName, fileName: $fileName, filePath: $filePath, directory: $directory, creationTime: $creationTime, creationTimeStamp: $creationTimeStamp, group: $group, queue: $queue, status: $status, progress: $progress, width: $width, height: $height, bitrate: $bitrate, duration: $duration, downloadedBytes: $downloadedBytes, totalLength: $totalLength, totalSegments: $totalSegments, downloadedSegments: $downloadedSegments}';
  }
}
